<html>

<head>
	<title>闻达</title>
	<meta charset="utf-8">
	<meta name="apple-mobile-web-app-capable" content="yes" />
	<meta name="mobile-web-app-capable" content="yes" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<meta name="viewport" content="width=400,  user-scalable=no">
	<link rel="shortcut icon" href="#" />
	<!-- <link href="https://npm.elemecdn.com/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet">
	<link href="https://npm.elemecdn.com/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
	<script src="https://npm.elemecdn.com/vue@2.x/dist/vue.min.js"></script>
	<script src="https://npm.elemecdn.com/vuetify@2.x/dist/vuetify.min.js"></script> -->

	<link href="static/mdi_font/css/materialdesignicons.min.css" rel="stylesheet">

	<link href="static/vuetify.min.css" rel="stylesheet">
	<script src="static/vue.js"></script>
	<script src="static/vuetify.min.js"></script>

	<script src="static/markdown-it.min.js"></script>

	<style>
		.v-navigation-drawer__content::-webkit-scrollbar {
			width: 5px;
		}

		.v-navigation-drawer__content::-webkit-scrollbar-track {
			background-color: #550056;
		}

		/* 滚动条的滑轨背景颜色 */

		.v-navigation-drawer__content::-webkit-scrollbar-thumb {
			background-color: rgba(0, 0, 0, 0.2);
		}

		/* 滑块颜色 */

		.v-navigation-drawer__content::-webkit-scrollbar-button {
			background-color: #7a007c;
		}

		/* 滑轨两头的监听按钮颜色 */
		.answer th,
		.answer td {
			outline: 1px solid;
			padding: 2px;
		}

		.ask,
		.answer {
			padding: 1em;
			background: #0003;
			border-radius: 16px;
			white-space: pre-line;
			max-width: 80%;
			margin: 0 10px;
			word-break: break-word;
		}

		.ask {
			margin-left: auto;
		}

		.头像 {
			margin-bottom: auto;
			color: #fff !important;
			position: unset;
		}

		#app,
		.v-application--wrap {
			background: transparent;
			min-height: 100%;
			background: #cecece0a;
		}

		.float {
			position: absolute !important;
		}

		header {
			position: fixed !important;
			top: 0;
			z-index: 1;
			width: 100%;
		}
		footer{
			padding-bottom: 54px!important;
		}
		.v-tabs {
			position: fixed !important;
			bottom: 0;
			z-index: 100;
			width: 100%;
		}

		[v-cloak] {
			display: none;
		}

		.v-application p {
			margin-bottom: 0;
		}

		.v-tabs-bar {
			background-color: #272727 !important;
		}

		.v-tabs-items {
			height: 100%;
		}

		.v-window-item.v-window-item--active {
			margin: 20px;
		}

		.v-tab.v-tab {
			color: inherit;
			font-weight: bold;
			font-size: large;
		}

		.user-avatar {
			background-color:  #0000a0;
		}

		.bot-avatar {
			background-color:  #7a0099
		}

	</style>
</head>

<body>
	<div id="app" v-cloak>
		<v-app>
			<v-navigation-drawer width="350" v-model="drawer" style="height: 100%;position: fixed;" temporary dark>
				<v-list>
					<v-list-item>
						<v-list-item-content>
							<v-list-item-title class="text-h6">
								闻达 Auto
							</v-list-item-title>
							<br>
							<v-list-item-subtitle>
								功能选择
							</v-list-item-subtitle>
						</v-list-item-content>
					</v-list-item>

					<v-divider></v-divider>

					<v-list-item v-for="功能 in 功能菜单" @click.stop="载入功能(功能)">
						<v-list-item-title v-text="功能.名称">
						</v-list-item-title>
					</v-list-item>
				</v-list>
			</v-navigation-drawer>
			<v-toolbar float dark>
				<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
				<v-toolbar-title>闻达</v-toolbar-title>
				<v-spacer></v-spacer>
				<v-switch hide-details color="purple" inset v-model="zhishiku" label="知识库"></v-switch>
				&nbsp;&nbsp;&nbsp;&nbsp;
				<v-switch hide-details color="purple" inset v-model="历史对话" label="历史"></v-switch>
				&nbsp;&nbsp;&nbsp;&nbsp;
				<v-btn color="#555"  dark size="x-large"
				 @click="对话=[]"
				:loading="loading">
				清除历史
			</v-btn>
			</v-toolbar>
			<br><br><br>

			<v-tabs-items v-model="tab">
				<v-tab-item key="对话">
					<div v-for="(当前对话, index) in 对话" :key="index"
						:class="['d-flex flex-row align-center my-2', 当前对话.role == 'user' ? 'justify-end': null]">
						<span v-if="当前对话.role == 'user'" class="ask" v-text="当前对话.content"></span>
						<v-hover v-slot="{ hover }">
							<div>
								<v-avatar :class="当前对话.role == 'user' ? 'user-avatar': 'bot-avatar'" size="36"
									class="头像">
									<span>{{ {user:"问",AI:'答'}[当前对话.role] }}</span>
								</v-avatar>
								<br>
								<v-expand-transition>
									<v-icon class="float" v-if="hover&&!loading" large text @click="删除 (当前对话)">
										mdi-delete
									</v-icon>
								</v-expand-transition>
							</div>
						</v-hover>

						<span v-if="当前对话.role != 'user'" class="answer" v-html="md2html(当前对话.content)"></span>
					</div>
					<br><br><br><br><br><br><br><br><br>
					<v-footer fixed>
						<v-textarea :loading="loading" :disabled="loading" no-resize :label="会话模式.描述"
							@keypress.enter="提交()" v-model="问题" height="120">
						</v-textarea>
						<div>
							<v-expand-transition>
								<div v-if="loading">
									<v-btn color="purple" rounded="lg" dark size="x-large" @click="中断()">
										中断
									</v-btn>
									<br>
								</div>
							</v-expand-transition>
							<v-btn style="margin: 10px 0;" color="purple" rounded="lg" dark size="x-large" @click="提交()"
								:loading="loading">
								发送
							</v-btn>
							<br>
							<v-expand-transition>
								<v-chip v-if="会话模式.名称!='常规模式'" color="purple" outlined close
									@click:close='会话模式={名称: "常规模式",描述: "输入问题",问题: ""}'>
									{{会话模式.名称}}
								</v-chip>
							</v-expand-transition>

						</div>
					</v-footer>
				</v-tab-item>
				<v-tab-item key="知识库">
					<v-btn color="purple" dark size="x-large" @click="find(prompt())">
						测试
					</v-btn>
					<v-data-table :headers="[{ text: '来源', value: 'title' },{ text: '内容', value: 'content' },]"
						:items="知识库" hide-default-header hide-default-footer></v-data-table>
				</v-tab-item>
				<v-tab-item key="设置">
					<br>
					<v-form>
						<v-slider :min="1000" :max="4000" :step="10" v-model="max_length" thumb-color="purple"
							label="max_length" thumb-label><template v-slot:append>
								<v-text-field v-model="max_length" class="mt-0 pt-0" type="number" style="width: 60px">
								</v-text-field>
							</template>
						</v-slider>
						<v-slider :min="0" :max="1" :step="0.1" v-model="temperature" thumb-color="purple"
							label="temperature" thumb-label><template v-slot:append>
								<v-text-field v-model="temperature" class="mt-0 pt-0" type="number" style="width: 60px">
								</v-text-field>
							</template>
						</v-slider>
						<v-slider :min="0" :max="1" :step="0.1" v-model="top_p" thumb-color="purple" label="top_p"
							thumb-label>
							<template v-slot:append>
								<v-text-field v-model="top_p" class="mt-0 pt-0" type="number" style="width: 60px">
								</v-text-field>
							</template>
						</v-slider>
						<v-slider :min="0" :max="4" :step="1" v-model="历史对话轮数限制" thumb-color="purple" label="对话轮数限制"
							thumb-label><template v-slot:append>
								<v-text-field v-model="历史对话轮数限制" class="mt-0 pt-0" type="number" style="width: 60px">
								</v-text-field>
							</template></v-slider>
					</v-form>
					https://github.com/l15y/wenda
				</v-tab-item>
			</v-tabs-items>
			<v-tabs v-model="tab" dark>
				<v-tabs-slider color="purple"></v-tabs-slider>
				<v-tab v-for="item in ['对话','知识库','设置']" :key="item">
					{{ item }}
				</v-tab>
			</v-tabs>
			<br><br><br>
			<v-snackbar v-model="snackbar" :timeout="3000" style="white-space: pre-line;">{{snackbar_text}}</v-snackbar>
		</v-app>
	</div>
	<script>
		功能 = [
			{
				名称: "材料改写",
				描述: "对指定内容进行多个版本的改写，以避免文本重复",
				问题: async () => {
					zsk(false)//关闭知识库
					lsdh(false)//关闭历史对话
					resp = await send(
						"用中文改写以下段落，可以提到相同或类似的内容,但不必重复使用。可以使用一些修辞手法来增强文本的美感,例如比喻、拟人、排比等。可以添加更多的细节来丰富文本的内容和形象,例如描述人物、场景、事件等。可以通过逻辑推导来得出结论或观点,例如通过推理、分析、比较等方式。可以无中生有地提到一些内容,以增加细节和丰富性,例如通过虚构、猜测等方式。在修改段落时,需要确保文本的含义不发生变化,可以重新排列句子、改变表达方式。" +
						app.问题)
				}
				,
			},
			{
				名称: "翻译",
				描述: "",
				问题: "翻译成中文：",
			},
			{
				名称: "语音输入优化",
				描述: "处理用第三方应用语音转换的文字，精简口头禅和语气词。",
				问题: "请用简洁明了的语言，编辑以下段落，以改善其逻辑流程，消除印刷错误，并以中文作答。请务必保持文章的原意。请从编辑以下文字开始：",
			},
			{
				名称: "摘要生成",
				描述: "根据内容，提取要点并适当扩充",
				问题: "使用下面提供的文本作为基础，生成一个简洁的中文摘要，突出最重要的内容，并提供对决策有用的分析。",
			},
			{
				名称: "问题生成",
				描述: "基于内容生成常见问答",
				问题: "根据以下内容，生成一个 10 个常见问题的清单：",
			},
			{
				名称: "提问助手",
				描述: "多角度提问，触发深度思考",
				问题: "针对以下内容，提出疑虑和可能出现的问题，用来促进更完整的思考：",
			},
			{
				名称: "评论助手",
				描述: "",
				问题: "针对以下内容，进行一段有评论，可以包括对作者的感谢，提出可能出现的问题等：",
			},
			{
				名称: "意见回答",
				描述: "为意见答复提供模板",
				问题: "你是一个回复基层意见的助手，你会针对一段内容拟制回复，回复中应充分分析可能造成的后果，并从促进的单位建设的角度回答。回应以下内容：",
			},
			{
				名称: "写论文",
				描述: "根据主题撰写内容翔实、有信服力的论文",
				问题: async () => {
					zsk(false)//关闭知识库
					lsdh(false)//关闭历史对话
					let Q = app.问题
					resp = await send("根据以下主题，写一篇论文提纲：" +
						Q)
					app.max_length = 4096
					resps = resp.split("\n\n")
					for (i in resps) {
						await send("根据主题：" + Q +
							"\n提纲：" + resp +
							"\n对提纲中的下列段落进行撰写：" + resps[i])
					}
				},
			},
			{
				名称: "写提纲",
				描述: "",
				问题: "你是一个擅长思考的助手，你会把一个主题拆解成相关的多个子主题。请你使用中文，针对下列主题，提供相关的子主题。直接输出结果，不需要额外的声明：",
			},
			{
				名称: "内容总结",
				描述: "将文本内容总结为 100 字。",
				问题: "将以下文字概括为 100 个字，使其易于阅读和理解。避免使用复杂的句子结构或技术术语。",
			},
			{
				名称: "写新闻",
				描述: "根据主题撰写新闻",
				问题: "使用清晰、简洁、易读的语言写一篇新闻，主题为",
			},
		]
		app = new Vue({
			el: '#app',
			vuetify: new Vuetify(),
			data: () => ({
				问题: '',
				对话: JSON.parse(localStorage["对话历史"] || "[]"),
				历史对话: false,
				历史对话轮数限制: 3,
				功能菜单: 功能,
				会话模式: {
					名称: "常规模式",
					描述: "输入问题",
					问题: "",
				},
				知识库: [],
				tab: '对话',
				zhishiku: false,
				temperature: 0.9,
				max_length: 2048,
				top_p: 0.3,
				snackbar: false,
				snackbar_text: '',
				loading: false,
				drawer: false,
			}),
			methods: {
			}
		})
		alert = text => {
			app.snackbar_text = text//.replace(/\n/g,"<br>")
			app.snackbar = true
		}

		载入功能 = (功能) => {
			app.会话模式 = 功能
			app.drawer = false
		}
		删除 = (item) => {
			app.对话.splice(Math.floor(app.对话.indexOf(item) / 2) * 2, 2)
			保存()
		}
		保存 = () => {
			localStorage["对话历史"] = JSON.stringify(app.对话)
		}
		中断 = () => {
			controller.abort()
			controller = new AbortController();
			signal = controller.signal;
		}
		controller = new AbortController();
		signal = controller.signal;
		提交 = async () => {
			if (typeof app.会话模式.问题 == 'function') {
				app.会话模式.问题()
			} else {

				send(app.问题, app.会话模式.问题)
			}
		}
		send = async (s, 会话模式 = "") => {
			app.loading = true
			let QA_history
			if (app.历史对话) {
				QA_history = app.对话.concat()
				if (app.历史对话轮数限制 != 0 && app.对话.length > app.历史对话轮数限制) {
					alert(`历史信息过长，将仅保留最后${app.历史对话轮数限制}次对话记忆。设置为0不限制`)

					QA_history = app.对话.slice(app.对话.length - app.历史对话轮数限制 * 2)
				}
			}
			else {
				QA_history = []
			}
			已排队到 = false
			setTimeout(read_now, 3000)
			app.问题 = s.replace(/\n+/g, '\n')
			app.对话.push({ role: "user", content: app.问题 })
			let 当前会话 = { role: "AI", content: '……' }
			app.对话.push(当前会话)
			setTimeout(() =>
				window.scrollTo(0, document.body.scrollHeight)
				, 0)
			try {
				response = await fetch("/api/chat_stream", {
					signal: signal,
					method: 'post',
					body: JSON.stringify({
						prompt: 会话模式 + app.问题,
						keyword: app.问题,
						temperature: app.temperature,
						top_p: app.top_p,
						max_length: app.max_length,
						history: QA_history,
						zhishiku: app.zhishiku
					}),
					headers: {
						'Content-Type': 'application/json'
					}
				})
				app.问题 = ''
				const reader = response.body.getReader();
				let buffer = ''
				while (true) {
					const { value, done } = await reader.read();
					已排队到 = true
					let res = new TextDecoder("utf-8").decode(value)
					buffer += res
					while (buffer.indexOf("///") > -1) {
						if (buffer == '/././') {//应对网络问题
							done = true
							break
						}
						buffer = buffer.split("///")
						当前会话.content = buffer[buffer.length - 2]
						buffer = buffer[buffer.length - 1]
					}
					if (done) break
				}
			} catch { }

			app.loading = false
			已排队到 = true
			保存()
			return 当前会话.content
		}
		read_now = async () => {
			if (!已排队到) {
				response = await fetch("/api/chat_now", {
					method: 'get'
				})
				alert(await response.text())
				setTimeout(read_now, 3000)
			}
		}

		find = async (s) => {
			response = await fetch("/api/find", {
				method: 'post',
				body: JSON.stringify({
					prompt: s
				}),
				headers: {
					'Content-Type': 'application/json'
				}
			})
			let json = await response.json()
			console.table(json)
			app.知识库 = json
		}
		function md2html(conent) {
			return conent
			// let md = new markdownit();
			// return md.render(conent).replace(/<a /g, '<a target="_blank"')
		}
		zsk = (b) => {
			app.zhishiku = b
		}
		lsdh = (b) => {
			app.历史对话 = b
		}
	</script>
</body>

</html>